# vim:ts=4:sw=4:noet:
""" Main window view"""

import wx
import os
import sys
import logging
import webbrowser
from c.base.version import *
from c.base.platform import Platform
from c.base.attribute import attribute
from c.compiler.compiler import Compiler
from c.ui.wxw.ids import ids, shortcuts
from c.ui.wxw.console import ConsoleWindow
from c.ui.wxw.docwindow import DocumentationWindow
from c.ui.wxw.stacktrace import StackTraceWindow
from c.ui.wxw.simwindow2d import SimCanvas
from c.ui.wxw.classpanel import ClassPanel
from c.ui.wxw.editor import Editor
from c.ui.wxw.projectbrowser import *
from c.ui.wxw.platform import UITweaks
from c.ui.wxw.dialogs import NewProjectDialog, JumpToDialog
from c.ui.wxw.widgets import MainToolBar, MainMenu, MethodBar, MainSplitter, BaseFrame
from c.ui.wxw.introwindow import IntroWindow
from c.ui.maincontroller import MainController, UILogger

class MainWindow(BaseFrame):

	controller = attribute("_controller", "r")
	project = attribute("_project", "r")

	def __init__(self, args):
		if wx.Platform=="__WXMSW__": 
			os.chdir(os.path.dirname(args[0]))
		self.uilog = UILogger(self)
		self._controller = MainController(self, args)
		# create window
		BaseFrame.__init__(self, None, InfoStrings["shortname"])
		self.SetMinSize((550, 350))

		self.vertSplitter = MainSplitter(self)

		## start of panel source ----
		self.panelSource = wx.Panel(self.vertSplitter, -1)
		vbox = wx.BoxSizer(wx.VERTICAL)
		# method bar
		self.methodBar = MethodBar(self.panelSource)
		vbox.Add(self.methodBar, 0, wx.EXPAND)
		# editor
		self.srctext = Editor(self.panelSource)
		self.srctext.Enable(False)
		if wx.Platform=="__WXMAC__": 
			vbox.Add(self.srctext, 1, wx.EXPAND | wx.RIGHT, -1)
		else:
			vbox.Add(self.srctext, 1, wx.EXPAND)
		self.panelSource.SetSizer(vbox)

		## start of panel sim ----
		self.panelSim = SimCanvas(self.vertSplitter, self.controller)
		self.panelSim.Show(False)

		## start of panel sim ----
		self.panelClass = ClassPanel(self.vertSplitter, self.controller)
		self.panelClass.Show(False)

		self.curPanel = "src"
	
		self.panelLeft = wx.Panel(self.vertSplitter, -1)
		vbox = wx.BoxSizer(wx.VERTICAL)
		# project browser
		self._project = ProjectBrowser(self.panelLeft, self, self.controller.mem)
		vbox.Add(self._project, 1, wx.EXPAND, UITweaks.divpad)
		self.panelLeft.SetSizer(vbox)

		# splitter
		self.vertSplitter.SplitVertically(self.panelLeft, self.panelSource)
		self.panelLeft.SetFocus()
		# toolbar
		self.toolBar = MainToolBar(self)
		self.SetToolBar(self.toolBar)
		self.toolBar.Realize()
		# main menu
		self.menuBar = MainMenu(self)
		self.menuBar.Check(ids.PROJECT_EDIT_LIBRARY, self.controller.isLibraryEditable())
		self.menuBar.Check(ids.PROJECT_AUTO_SWITCH, self.controller.shouldAutoSwitch())
		self.SetMenuBar(self.menuBar)  
		self.Bind(wx.EVT_MENU, self.OnMenuClick)
		uiUpdates = [ 
				ids.FILE_SAVE, ids.FILE_SAVE_AS, ids.EDIT_UNDO, ids.EDIT_REDO,
				ids.EDIT_CUT, ids.EDIT_COPY, ids.EDIT_PASTE,
				ids.VIEW_JUMPTO, ids.PROJECT_EDIT_LIBRARY, ids.PROJECT_EXPORT_ALL,
				ids.PROJECT_IMPORT_CLASS, ids.PROJECT_NEW_CLASS, ids.PROJECT_NEW_METHOD,
				ids.PROJECT_COMPILE ]
		for uid in uiUpdates:
			self.Bind(wx.EVT_UPDATE_UI, self.onUpdateUI, id=uid)
		self.Bind(wx.EVT_CLOSE, self.onQuit)
		# status bar
		self.statusBar = wx.StatusBar(self, -1)
		self.SetStatusBar(self.statusBar)
		# set button states
		self.setButtonStates(0, 0, 0)
		# misc setup

		self.shuttingDown = False
		self.viewMode = "empty"
		self.controller.onStartComplete()

		self.Connect(-1, -1, EVT_UISTATE_ID, self.uiStateChange)
		self.Connect(-1, -1, EVT_DEVICEUPGRADE_ID, self.onDeviceUpgrade)
		self.Connect(-1, -1, EVT_DEVICECHANGE_ID, self.onDeviceChange)
		self.Connect(-1, -1, EVT_THREADEXCEPTION_ID, self.onThreadException)
		self.Connect(-1, -1, EVT_COMPILE_ID, self.onCompileDone)
		self._project.controller.populate()
		self.cw = ConsoleWindow(self)
		self.docs = DocumentationWindow(self)
		self.stacktrace = StackTraceWindow(self, self.controller.r, self.controller.mem)
		self.Show(True)
		self.Layout()
		self.Fit()
		for menu, label in self.menuBar.GetMenus():
			menu.UpdateUI()
		for f in os.listdir(os.path.join(Platform.getDataPath(), "examples")):
			if len(f)>0 and f[0]!=".":
				self.menuBar.addExample(f.split(".")[0], f)
		self.vertSplitter.SetSashPosition(180)
		if self.controller.saveState.getOpt("firststart", True):
			self.onFirstStart()
			self.controller.saveState.setOpt("firststart", False)
		elif self.controller.saveState.getOpt("lastproj"):
			lastMeth = self.controller.saveState.getOpt("lastmeth")
			if lastMeth:
				self._project.controller.jumpTo(lastMeth)
		logging.info("Foxtalk ready. (Python %s, WX %s)" % (
			".".join(map(str, sys.version_info[:3])), wx.__version__))
		self.controller.checkProjectUpgrade()

	##############################################################################

	def setRightPanel(self, which):
		if self.curPanel == which:
			return
		if which not in ["src", "class", "sim"]:
			logging.error("Failing to change right panel to '%s'" % which)
			return
		sashPos = self.vertSplitter.GetSashPosition()
		self.panelSource.Show(which=="src")
		self.panelClass.Show(which=="class")
		self.panelSim.Show(which=="sim")
		self.vertSplitter.Unsplit()
		panels = {"src" : self.panelSource, 
							"sim" : self.panelSim, 
							"class" : self.panelClass }
		self.vertSplitter.SplitVertically(self.panelLeft, panels[which])
		self._project.Update()
		self.vertSplitter.SetSashPosition(sashPos)
		self.curPanel = which

	def setStatusText(self, msg):
		self.statusBar.SetStatusText(msg)

	def setButtonStates(self, comp, go, stop):
		self.menuBar.Enable(ids.PROJECT_COMPILE, comp)
		self.toolBar.EnableTool(ids.PROJECT_COMPILE, comp)
		self.menuBar.Enable(ids.PROJECT_RUN, go)
		self.toolBar.EnableTool(ids.PROJECT_RUN, go)
		self.menuBar.Enable(ids.PROJECT_STOP, stop)
		self.toolBar.EnableTool(ids.PROJECT_STOP, stop)

	def updateTitle(self):
		name = InfoStrings["shortname"]
		projname = os.path.basename(self.controller.projectFilename)
		title = "%s - %s" % (name, projname)
		if self.viewMode == "source":
			title += " - %s" % self.titlestring
		self.SetTitle(title)

	def setSourceView(self, titlestring=""):
		if self.shuttingDown:
			return
		self.viewMode = "source"
		self.srctext.SetFocus()
		if titlestring:
			self.titlestring = titlestring
			self.updateTitle()

	def setEmptyView(self):
		if self.shuttingDown:
			return
		self.viewMode = "empty"
		self.updateTitle()

	def uiStateChange(self, evt):
		t = evt.type
		d = evt.data
		if t=="progress-update":
			percent = d # unimplemented
		elif t=="button":
			comp, run, stop, insp = d[:4]
			if not self.controller.dm.currentConnection():
				run = stop = 0
			self.setButtonStates(comp, run, stop)
		elif t=="button-msg":
			comp, run, stop, insp = d[:4]
			if not self.controller.dm.currentConnection():
				run = stop = 0
			self.setButtonStates(comp, run, stop)
			txt = d[4]
			self.setStatusText(txt)
		elif t=="trace":
			txt = d
			self.setStatusText(txt)
			msg = self.controller.checkBotState([])
			self.stacktrace.showBacktrace(msg)
		elif t=="change-view":
			if d=="sim":
				self.jumpTo("/robot/sim:tcp")
			else:
				logging.warn("Unhandled change-view: %s" % d)
		elif t=="post-run":
			d()
		elif t=="start":
			self.stacktrace.hide()
		elif t=="update-available":
			dial = wx.MessageDialog(self,
					"Version %s is available, do you want to visit the %s site to download it now?" % (
						d.sparkle["version"], InfoStrings["shortname"]),
					"New Version Available",
					style = wx.YES_NO|wx.ICON_QUESTION)
			val = dial.ShowModal()
			if val == wx.ID_YES:
				webbrowser.open(InfoStrings["urlDownload"])
		else:
			logging.warn("Unhandled ui state change: %s %s" % (t, d))
	
	def jumpTo(self, value):
		self._project.controller.jumpTo(value)

	def logClear(self):
		wx.PostEvent(self.cw.console, LogMsgEvent("~~clear~~"))

	def logMessage(self, msg):
		wx.PostEvent(self.cw.console, LogMsgEvent(msg))

	def showSource(self, cls, meth, line):
		if self._project.controller.showSource(cls, meth, line):
			self.srctext.markLine(line)
		
	def makeFileDialog(self, title, dir=None, filename="", filter="", type=0):
		if not dir:
			dir = self.controller.defaultDir
		return wx.FileDialog(self, title, dir, filename, filter, type)

	def postEvent(self, evt):
		wx.PostEvent(self, evt)

	##############################################################################

	def onProjectSelectItem(self, item):
		self.docs.setItem(item)
	
	def onUpdateUI(self, event):
		id = event.GetId()
		projOpen = (self.controller.projectFilename!="")
		if (id == ids.FILE_SAVE or
				id == ids.FILE_SAVE_AS or
				id == ids.VIEW_JUMPTO or
				id == ids.PROJECT_EDIT_LIBRARY or
				id == ids.PROJECT_AUTO_SWITCH or
				id == ids.PROJECT_EXPORT_ALL or
				id == ids.PROJECT_IMPORT_CLASS or
				id == ids.PROJECT_NEW_CLASS or
				id == ids.PROJECT_NEW_METHOD):
			event.Enable(projOpen)
		elif id == ids.EDIT_CUT:   event.Enable(projOpen and self.srctext.canCut())
		elif id == ids.EDIT_COPY:  event.Enable(projOpen and self.srctext.canCopy())
		elif id == ids.EDIT_PASTE: event.Enable(projOpen and self.srctext.canPaste())
		elif id == ids.EDIT_UNDO:  event.Enable(projOpen and self.srctext.CanUndo())
		elif id == ids.EDIT_REDO:  event.Enable(projOpen and self.srctext.CanRedo())
		elif id == ids.PROJECT_COMPILE:
			item = self._project.controller.getSelectedItem()
			#event.Enable(item!=None and item.canCompile())
			comp = (item!=None and item.canCompile())
			self.menuBar.Enable(ids.PROJECT_COMPILE, comp)
			self.toolBar.EnableTool(ids.PROJECT_COMPILE, comp)
		else:
			logging.warn("unhandled update ui event %s" % event)
	
	def OnToolClick(self, event):
		self.OnMenuClick(event)

	def OnMenuClick(self, event):
		idmap = {
				ids.FILE_EXIT:              self.onQuit,
				ids.FILE_NEW_PROJECT:       self.onNewProjectButton,
				ids.FILE_OPEN:              self.onOpenButton, 
				ids.FILE_SAVE:              self.onSaveButton,
				ids.FILE_SAVE_AS:           self.onSaveAsButton,

				ids.EDIT_UNDO:              self.onUndoButton,
				ids.EDIT_REDO:              self.onRedoButton,
				ids.EDIT_CUT:               self.onCutButton,
				ids.EDIT_COPY:              self.onCopyButton,
				ids.EDIT_PASTE:             self.onPasteButton,

				ids.PROJECT_COMPILE:        self.onCompileButton,
				ids.PROJECT_RUN:            self.onGoButton,
				ids.PROJECT_STOP:           self.onStopButton,
				ids.PROJECT_NEW_METHOD:     self.onNewMethodButton,
				ids.PROJECT_NEW_CLASS:      self.onNewClassButton,
				ids.PROJECT_EXPORT_ALL:     self.onExportAllButton,
				ids.PROJECT_EXPORT_CLASS:   self.onExportClassButton,
				ids.PROJECT_IMPORT_CLASS:   self.onImportClassButton,
				ids.PROJECT_EDIT_LIBRARY:   self.onEditLibraryButton,
				ids.PROJECT_AUTO_SWITCH:    self.onAutoSwitchButton,

				ids.VIEW_CONSOLE:           self.onConsoleButton,
				ids.VIEW_JUMPTO:            self.onJumpToButton,
				ids.VIEW_GET_INFO:          self.onGetInfoButton,
				ids.VIEW_DOCUMENTATION:     self.onDocsButton,

				ids.DEV_CRASH:              self.onCrashButton,

				ids.HELP_ABOUT:             self.onAboutButton,
				ids.HELP_REFERENCE:         self.onReferenceButton,
				ids.HELP_TUTORIAL:          self.onTutorialButton,
		}
		id = event.GetId()
		if id in idmap:
			idmap[id](event)
		elif id in self.menuBar.examples:
			self.openExample(os.path.join(Platform.getDataPath(), "examples/%s" % self.menuBar.examples[id]))
		else:
			logging.warn("unhandled menu click: %s" % id)

	def openExample(self, fn):
		self.checkForChanges()
		self.controller.openProject(fn)

	def checkForChanges(self):
		if self._project.controller.changed or self.srctext.hasChanged() or self.docs.hasChanged():
			dial = wx.MessageDialog(self,
					"Do you want to save the changes made in the project \"%s\"" % (
						os.path.basename(self.controller.projectFilename)),
					"Save Changes?",
					style = wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
			val = dial.ShowModal()
			if val == wx.ID_YES:
				self.controller.saveProject()
			return val

	def onQuit(self, event):
		if self.checkForChanges() == wx.ID_CANCEL:
			return None
		self.Show(False)
		self.controller.onShutdown()
		self.shuttingDown = True
		self.controller.dm.shutdown()
		self.Destroy()

	def onUndoButton(self, event):
		self.srctext.Undo()

	def onRedoButton(self, event):
		self.srctext.Redo()

	def onCutButton(self, event):
		self.srctext.CmdKeyExecute(wx.stc.STC_CMD_CUT)

	def onCopyButton(self, event):
		self.srctext.CmdKeyExecute(wx.stc.STC_CMD_COPY)

	def onPasteButton(self, event):
		self.srctext.CmdKeyExecute(wx.stc.STC_CMD_PASTE)

	def onExportAllButton(self, event):
		self.controller.mem.fileOut("src")

	def onExportClassButton(self, event):
		self._project.OnExportClass(event)

	def onImportClassButton(self, event):
		dial = self.makeFileDialog("Import Class", filter="*.st", type=wx.FD_OPEN)
		res = dial.ShowModal()
		if res == wx.ID_OK:
			srcfile = dial.GetDirectory()+"/"+dial.GetFilename()
			self._project.controller.importClass(srcfile)
			self.setStatusText("imported %s" % (srcfile))

	def onAboutButton(self, event):
		info = wx.AboutDialogInfo()
		info.SetName(InfoStrings["longname"])
		copy = InfoStrings["copyright"]
		if wx.Platform=="__WXMSW__":
			copy = copy.replace("&", "&&")
		info.SetCopyright(copy)
		if wx.Platform=="__WXMAC__":
			info.SetDescription(InfoStrings["description"])
		else:
			info.SetDescription(InfoStrings["description"])
			info.SetVersion(InfoStrings["versionString"])
			info.SetWebSite(InfoStrings["urlHomepage"])
		wx.AboutBox(info)

	def onReferenceButton(self, event):
		self.controller.openHTMLDocs("reference.html")

	def onTutorialButton(self, event):
		self.controller.openHTMLDocs("tutorial.html")

	def onNewProjectButton(self, event):
		self.checkForChanges()
		dialog = NewProjectDialog(self)
		res = dialog.ShowModal()
		if res == wx.ID_OK:
			projtype, projname = dialog.GetValue()
			mainclass = None
			if projtype == "Drawing Project":
				mainclass = "DrawingRobot"
			elif projtype == "Basic Project":
				mainclass = "Robot"
			try:
				self.controller.newProject(projname, mainclass)
			except:
				dialog.Destroy()
				dial = wx.MessageDialog(self,
					"Couldn't find default project file, install is probably corrupt",
					"Missing Default Project",
					style = wx.OK|wx.ICON_QUESTION)
				dial.ShowModal()
				return
			self.controller.projectFilename = os.path.join(
					self.controller.defaultDir, 
					projname+"."+InfoStrings["imageExtension"])
			self.controller.dm.refresh()
			self.updateTitle()
			self.controller.firstSave = True
		dialog.Destroy()
		self.setButtonStates(1, 1, 0)

	def onOpenButton(self, event):
		self.checkForChanges()
		dial = self.makeFileDialog(
						"Open Project", 
						filename=self.controller.projectFilename, 
						dir=self.controller.defaultDir,
						filter="*.%s" % InfoStrings["imageExtension"], 
						type=wx.FD_OPEN)
		res = dial.ShowModal()
		if res == wx.ID_OK:
			self.controller.openProject(dial.GetPath())

	def onSaveButton(self, event):
		if self.controller.firstSave:
			self.onSaveAsButton(event)
		else:
			self.controller.saveProject()
	
	def onSaveAsButton(self, event):
		dial = self.makeFileDialog(
						"Save Project", 
						filename=os.path.basename(self.controller.projectFilename), 
						dir=self.controller.defaultDir,
						filter="*.%s" % InfoStrings["imageExtension"], 
						type=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
		res = dial.ShowModal()
		if res == wx.ID_OK:
			self.controller.projectFilename = dial.GetPath()
			self.controller.saveProject()
			self.controller.firstSave = False

	def onCompileButton(self, event):
		selitem = self._project.controller.getSelectedItem()
		if selitem:
			selitem.deselect(forceCompile=True)

	def onStopButton(self, event):
		self.controller.doStop()

	def onGoButton(self, event):
		selitem = self._project.controller.getSelectedItem()
		if selitem:
			selitem.deselect(forceCompile=True, runAfter=True)

	def onConsoleButton(self, event):
		self.cw.toggleVisible()
		
	def onDocsButton(self, event):
		self.docs.Show(True)
	
	def onNewMethodButton(self, event):
		self._project.OnNewMethod(event)
	
	def onNewClassButton(self, event):
		self._project.OnNewClass(event)
	
	def onEditLibraryButton(self, event):
		editable = self.menuBar.FindItemById(ids.PROJECT_EDIT_LIBRARY).IsChecked()
		self._project.setLibraryEditable(editable)
		self.controller.onEditLibraryButton(editable)

	def onAutoSwitchButton(self, event):
		autoswitch = self.menuBar.FindItemById(ids.PROJECT_AUTO_SWITCH).IsChecked()
		self.controller.onAutoSwitchButton(autoswitch)

	def onGetInfoButton(self, event):
		self._project.OnGetInfo(event)
	
	def onJumpToButton(self, event):
		dialog = JumpToDialog(self)
		res = dialog.ShowModal()
		if res == wx.ID_OK:
			self.jumpTo(dialog.GetValue())
		dialog.Destroy()

	def onFirstStart(self):
		IntroWindow(self).ShowModal()

	def onCrashButton(self, event):
		raise Exception("CRASH")

	def onImportFailed(self, fn):
		dial = wx.MessageDialog(self,
				"Failed to import file '%s'" % fn,
				"Import Failed",
				style = wx.OK|wx.ICON_ERROR)
		val = dial.ShowModal()

	def onLoadFailed(self, excp):
		dial = wx.MessageDialog(self,
				"The project '%s' appears to be corrupt and could not be opened" % (
					os.path.basename(self.controller.projectFilename)),
				"Failed to Open Project",
				style = wx.OK|wx.ICON_ERROR)
		val = dial.ShowModal()

	def onProjectUpgrade(self):
		dial = wx.MessageDialog(self,
				"A new Foxtalk library is available, would you like to upgrade this project?",
				"Upgrade Project '%s'?" % os.path.basename(self.controller.projectFilename),
				style = wx.YES_NO|wx.ICON_QUESTION)
		val = dial.ShowModal()
		if val == wx.ID_YES:
			self._project.controller.upgradeProject()
			self.controller.dm.refresh()

	def onDeviceUpgrade(self, evt):
		dial = wx.MessageDialog(self,
				"New firmware (r%d) is available for the connected device, would you like to upgrade it?" % evt.data[0],
				"Upgrade Firmware?",
				style = wx.YES_NO|wx.ICON_QUESTION)
		val = dial.ShowModal()
		if val == wx.ID_YES:
			self.controller.doUpgradeStart()
			dial = wx.MessageDialog(self,
					"Please reboot the connected device into bootloader mode then click OK to begin the upgrade.",
					"Enter Bootloader Mode",
					style = wx.OK|wx.CANCEL|wx.ICON_QUESTION)
			val = dial.ShowModal()
			if val == wx.ID_OK:
				self.cw.show()
				self.controller.doUpgrade(evt.data[1])

	def onDeviceChange(self, evt):
		if evt.conn:
			self._project.controller.addRobot(evt.id, evt.name, evt.conn)
		else:
			self._project.controller.removeRobot(evt.id)

	def onCompileDone(self, evt):
		self._project.controller.compileDone(evt)

	def onThreadException(self, evt):
		raise evt.data
